package cn.xeblog.plugin.action;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.xeblog.commons.entity.User;
import cn.xeblog.commons.entity.UserMsgDTO;
import cn.xeblog.commons.enums.Action;
import cn.xeblog.plugin.cache.DataCache;
import cn.xeblog.plugin.enums.Command;
import cn.xeblog.plugin.listener.MainWindowInitializedEventListener;
import cn.xeblog.plugin.ui.MainWindow;
import cn.xeblog.plugin.util.CommandHistoryUtils;
import cn.xeblog.plugin.util.UploadUtils;
import com.intellij.ui.components.JBList;
import com.intellij.ui.components.JBScrollPane;
import org.apache.commons.lang3.StringUtils;

import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.*;
import java.util.List;

/**
 * 输入行为
 *
 * @author anlingyi
 * @date 2022/8/4 9:48 PM
 */
public class InputAction implements MainWindowInitializedEventListener {

  /**
   * 内容区域(输入区域)
   */
  private static JTextArea contentArea;
  /**
   * 左边顶部面板（用来显示命令列表和@用户列表的）
   */
  private static JPanel leftTopPanel;
  /**
   * 用于展示提示列表框,相当于JList(列表框组件)，只不过这个是idea提供的组件。
   */
  private static JBList jbList;
  /**
   * 是否主动触发
   */
  private static boolean isProactive;

  /**
   * 冻结时间
   */
  private final static long FREEZE_TIME = 15 * 1000;
  /**
   * 间隔时间
   */
  private final static long INTERVAL_TIME = 10 * 1000;

  /**
   * 冻结结束时间
   */
  private static long freezeEndTime;

  /**
   * 消息发送计数
   */
  private static int sendCounter = -1;

  /**
   * 消息发送计数开始时间
   */
  private static long sendCounterStartTime;

  @Override
  public void afterInit(MainWindow mainWindow) {
    // 初始化组件
    contentArea = mainWindow.getContentArea();
    leftTopPanel = mainWindow.getLeftTopPanel();

    // 绑定键盘监听
    bindKeyListener();
  }

  /**
   * 绑定键盘监听
   */
  private static void bindKeyListener() {
    // 输入区域监听
    contentArea.addKeyListener(new KeyAdapter() {
      @Override
      public void keyPressed(KeyEvent e) {
        // 回车，发送消息
        if (KeyEvent.VK_ENTER == e.getKeyCode()) {
          // 阻止默认事件
          e.consume();
          // 发送消息
          sendMsg();
        }

        // 跳过
        if (e.getKeyCode() == KeyEvent.VK_TAB && leftTopPanel.isVisible()) {
          e.consume();
        }

        // 输入的内容
        String content = contentArea.getText();

        // 上、下选择时
        if (e.getKeyCode() == 38 || e.getKeyCode() == 40) {
          // 阻止默认事件
          e.consume();
          // 主动触发 并且提示框还可见，并且列表不为空时
          if (isProactive && leftTopPanel.isVisible() && jbList != null) {
            // 获得输入焦点
            jbList.requestFocus();
          } else if (StrUtil.isBlank(content) || content.startsWith(Command.COMMAND_PREFIX)) {
            // 输入区域内容为空或者以命令前缀开关时
            String cmd = null;
            // 获取上一个历史命令
            if (e.getKeyCode() == 38) {
              cmd = CommandHistoryUtils.getPrevCommand();
            } else if (e.getKeyCode() == 40) {
              // 获得下一个历史命令
              cmd = CommandHistoryUtils.getNextCommand();
            }

            // 当命令不为空时
            if (StrUtil.isNotBlank(cmd)) {
              // 非主动
              isProactive = false;
              // 设置命令文本
              contentArea.setText(cmd);
            }
          }
        } else {
          // 设置为主动触发
          isProactive = true;
        }
      }

      // 键盘释放事件
      @Override
      public void keyReleased(KeyEvent e) {
        if ((e.isControlDown() || e.isMetaDown()) && e.getKeyCode() == KeyEvent.VK_V) {
          // 不在线，发送”提示登录“
          if (!DataCache.isOnline) {
            ConsoleAction.showLoginMsg();
            return;
          }

          // 粘贴图片
          pasteImage();
        } else {
          // @用户和命令提示
          atUserAndCommandTips(e);
        }
      }
    });
  }

    /**
     * 粘贴图片
     */
    private static void pasteImage() {
        // 获取系统剪贴板
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        // 获取数据内容（Transferable接口用于不同存储介质间数据传输）
        Transferable transferable = clipboard.getContents(null);
        try {
            // 判断当前用来传输数据的Transferable对象的数据传输格式是否符合DataFlavor对象中有的数据传输格式
            // 这里是文件列表格式
            if (transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
                // 只上传第一个文件
                List<File> fileList = (List<File>) transferable.getTransferData(DataFlavor.javaFileListFlavor);
                UploadUtils.uploadImageFile(fileList.get(0));
                clean();
            } else if (transferable.isDataFlavorSupported(DataFlavor.imageFlavor)) {
                // 这里是图片格式
                Image image = (Image) transferable.getTransferData(DataFlavor.imageFlavor);
                // 只上传第一个图片
                UploadUtils.uploadImage(image);
                // 清空数据格式
                clean();
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    /**
     * ‘@’用户和命令提示
     *
     * @param e 事件
     */
    private static void atUserAndCommandTips(KeyEvent e) {
        // 是否为@开头
        boolean isAt = false;
        // 数据列表
        List<String> dataList = null;
        // 文本内容
        String content = contentArea.getText();
        // 光标位置
        int caretPosition = contentArea.getCaretPosition();
        int atIndex = -1;
        // 命令前缀
        String commandPrefix = Command.COMMAND_PREFIX;
        // 以命令前缀开头
        if (content.startsWith(commandPrefix)) {
            // 渲染命令map
            Map<String, String> commandMap = new LinkedHashMap<>();
            for (Command command : Command.values()) {
                commandMap.put(command.getCommand(), command.getCommand() + " (" + command.getDesc() + ")");
            }

            String command = content.substring(1);
            if (StrUtil.isBlank(command)) {
                dataList = new ArrayList<>(commandMap.values());
            } else {
                // 匹配列表并转给赋值给dataList
                final List<String> matchList = new ArrayList<>();
                commandMap.forEach((k, v) -> {
                    if (k.toLowerCase().contains(command.toLowerCase()) || command.startsWith(k)) {
                        matchList.add(v);
                    }
                });
                dataList = matchList;
            }
        } else {
            // 当用户在线时，且不以命令前缀开头时
            if (DataCache.isOnline) {
                // 是否为@
                isAt = true;
                // 获取内容
                String atContent = content.substring(0, caretPosition);
                // 最后出现@的下标,防止可能有多个@
                atIndex = atContent.lastIndexOf("@");
                // 当存在@时
                if (atIndex > -1) {
                    // 获取在线用户列表
                    List<User> onlineUserList = new ArrayList<>(DataCache.userMap.values());
                    // 以角色来对用户排序
                    onlineUserList.sort((u1, u2) -> {
                        // o1,o2枚举中的顺序数
                        int o1 = u1.getRole().ordinal();
                        int o2 = u2.getRole().ordinal();
                        if (o1 < o2) {
                            return -1;
                        }
                        if (o1 == o2) {
                            return 0;
                        }
                        return 1;
                    });

          // 所有在线用户列表
          List<String> allUserList = new ArrayList<>();
          onlineUserList.forEach(user -> allUserList.add(user.getUsername()));

                    // 最后一个@后面的内容
                    String name = content.substring(atIndex + 1, caretPosition);
                    // 匹配用户,并加入数据列表
                    if (StrUtil.isNotBlank(name)) {
                        dataList = new ArrayList<>();
                        for (String user : allUserList) {
                            if (user.toLowerCase().contains(name.toLowerCase())) {
                                dataList.add(user);
                            }
                        }
                    }

                    if (atIndex + 1 == caretPosition && CollUtil.isEmpty(dataList)) {
                        dataList = allUserList;
                    }
                }
            }
        }

    // 提示框隐藏
    leftTopPanel.setVisible(false);
    leftTopPanel.removeAll();

    // 匹配的数据列表不为空
    if (CollectionUtil.isNotEmpty(dataList)) {
      // 是否为@
      boolean copyIsAt = isAt;
      // @的下标
      int copyAtIndex = atIndex;

      Runnable runnable = () -> {
        // 列表框为空 跳过
        if (jbList == null) {
          return;
        }

        // 选中的值,为空跳过
        Object selectedValue = jbList.getSelectedValue();
        if (selectedValue == null) {
          return;
        }

        String value = selectedValue.toString();
        //如果是@补全用户名@
        if (copyIsAt) {
          // 从@的下标+1处开始+value，即补全提示
          contentArea.replaceRange(value + " ", copyAtIndex + 1, caretPosition);
        } else {
          // 这里把命令后的括号全去除了，只补全命令
          contentArea.setText(value.substring(0, value.indexOf(" ")));
        }

        // 设置光标
        requestFocus();

        leftTopPanel.setVisible(false);
        leftTopPanel.removeAll();
      };

      // 没有提示列表框，先创建提示列表框
      jbList = new JBList();
      // 加载提示信息
      jbList.setListData(dataList.toArray());

      // 添加回车与~（esc下面的那个键）监听
      jbList.addKeyListener(new KeyAdapter() {
        @Override
        public void keyPressed(KeyEvent e) {
          if (KeyEvent.VK_ENTER == e.getKeyCode() || KeyEvent.VK_BACK_QUOTE == e.getKeyCode()) {
            runnable.run();
          }
        }
      });

      // 添加点击监听
      jbList.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
          if (e.getClickCount() == 2) {
            runnable.run();
          }
        }
      });

      // 给jbList设置滚动条
      JBScrollPane scrollPane = new JBScrollPane(jbList);
      scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
      scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);

      // 展示提示框
      leftTopPanel.setMinimumSize(new Dimension(0, 100));
      leftTopPanel.add(scrollPane);
      leftTopPanel.setVisible(true);

      // tab快捷键
      if (e.getKeyCode() == KeyEvent.VK_TAB) {
        String value = dataList.get(0);
        if (copyIsAt) {
          contentArea.replaceRange(value + " ", copyAtIndex + 1, caretPosition);
        } else {
          contentArea.replaceRange(value.substring(0, value.indexOf(" ")), 0, caretPosition);
        }
      }
    }

    // 重新渲染
    leftTopPanel.updateUI();
  }

  /**
   * 发送消息
   */
  private static void sendMsg() {
    // 发送文本
    String content = contentArea.getText();
    if (StringUtils.isEmpty(content)) {
      return;
    }

    // 超长提示
    if (content.length() > 200) {
      ConsoleAction.showSimpleMsg("发送的内容长度不能超过200字符！");
    } else {
      // 当以命令前缀开头时
      if (content.startsWith(Command.COMMAND_PREFIX)) {
        // 显示命令消息
        ConsoleAction.showSimpleMsg(content);
        // 命令处理
        Command.handle(content);
      } else {
        // 用户在线时
        if (DataCache.isOnline) {
          // 发送的次数 并且发送的消息间隔小于10秒
          if (sendCounter == 0 && System.currentTimeMillis() - sendCounterStartTime < INTERVAL_TIME) {
            sendCounterStartTime = 0;
            freezeEndTime = System.currentTimeMillis() + FREEZE_TIME;
          }

          // 在冻结结束之前发消息给提示
          long endTime = freezeEndTime - System.currentTimeMillis();
          if (endTime > 0) {
            ConsoleAction.showSimpleMsg("消息发送过于频繁，请于" + endTime / 1000 + "s后再发...");
            return;
          }

          String[] toUsers = null;
          // 通过正则匹配获取接收用户列表
          List<String> toUserList = ReUtil.findAll("(@)([^\\s]+)([\\s]*)", content, 2);
          // 存在接收者列表时
          if (CollectionUtil.isNotEmpty(toUserList)) {
            // 移除列表
            List<String> removeList = new ArrayList<>();
            // 移除离线用户列表
            for (String toUser : toUserList) {
              if (DataCache.getUser(toUser) == null) {
                removeList.add(toUser);
              }
            }
            // 存在离线用户时移除
            if (!removeList.isEmpty()) {
              toUserList.removeAll(removeList);
            }

            // 把@的用户加入到接收用户列表中
            if (!toUserList.isEmpty()) {
              toUserList.add(DataCache.username);
              toUsers = ArrayUtil.toArray(new HashSet<>(toUserList), String.class);
            }
          }

          // 初始化计数
          if (sendCounter == -1) {
            sendCounter = 0;
          }
          // 发送消息条数超过6条进行限制
          if (++sendCounter >= 6) {
            sendCounter = 0;
          }
          // 发送消息时开始计时
          if (sendCounter == 1) {
            sendCounterStartTime = System.currentTimeMillis();
          }

          // 消息发送
          MessageAction.send(new UserMsgDTO(content, toUsers), Action.CHAT);
        } else {
          // 登录提示
          ConsoleAction.showLoginMsg();
        }
      }
      clean();
    }
    // 当输入消息或命令时，如果此时控制台在最底部，那么控制台滚动条自动滚动到底端
    ConsoleAction.gotoConsoleLow();
  }

  /**
   * 清空输入区域
   */
  public static void clean() {
    contentArea.setText("");
  }

  /**
   * 设置焦点
   *
   * @return true, 设置成功，否则，false
   */
  public static boolean requestFocus() {
    // 设置焦点
    contentArea.requestFocusInWindow();
    // 只有是组件的所有者才能设置焦点
    return contentArea.isFocusOwner();
  }

  /**
   * 休息光标，作用是重新打开时，光标在输入区域最后位置
   *
   * @return true, 设置焦点成功，否则，false
   */
  public static boolean restCursor() {
    // 光标在输入区域最后位置
    int len = StrUtil.length(contentArea.getText());
    contentArea.setCaretPosition(len);
    return requestFocus();
  }

}
